package com.ruibang.glass.quality.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruibang.glass.common.constants.RedisKey;
import com.ruibang.glass.common.utils.AssertUtil;
import com.ruibang.glass.frequencycontrol.annotation.RedissonLock;
import com.ruibang.glass.frequencycontrol.util.RedisUtils;
import com.ruibang.glass.quality.constant.Constants;
import com.ruibang.glass.quality.constant.QualityErrorCode;
import com.ruibang.glass.quality.domain.req.LabelReq;
import com.ruibang.glass.quality.domain.req.LabelSerialNumReq;
import com.ruibang.glass.quality.domain.resp.LatestBindTime;
import com.ruibang.glass.quality.entity.Label;
import com.ruibang.glass.quality.mapper.LabelMapper;
import com.ruibang.glass.quality.service.LabelService;
import com.ruibang.glass.quality.util.ExcelStyleUtils;
import com.teaming.cloud.framework2.autoconfigure.redis.RedisSingleService;
import com.teaming.cloud.framework2.autoconfigure.redis.RedisSingleService;
import com.teaming.cloud.framework2.common.exception.ServiceException;
import com.teaming.cloud.framework2.common.model.ResultBody;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Author: songJinKang
 * @CreateTime: 2023-12-08  15:00
 * @Description: TODO
 * @Version: 1.0
 */
@Slf4j
@Service
public class LabelServiceImpl extends ServiceImpl<LabelMapper, Label> implements LabelService {


    @Autowired
    @Qualifier("myRedisTemplate")
    private RedisTemplate<String, Object> redisTemplate;

//    @Autowired
//    private RedisSingleService redisService;

    @Autowired
    private RedisSingleService redisService;

    /**
     * @Description: 生成标签
     * @version v1.0
     * @author songJinKang
     * @date 2023-12-08 15:08
     */
    @Override
    @RedissonLock(key = "#labelReq.grade")
    @Transactional(rollbackFor = Exception.class)
    public ResultBody getCode(LabelReq labelReq) {
        if (labelReq == null || StringUtil.isBlank(labelReq.getGrade()) ||
                StringUtil.isBlank(labelReq.getPalletNumber()) || labelReq.getDate() == null) {
            throw new ServiceException(QualityErrorCode.INVALID_PARAM.getCode(), QualityErrorCode.INVALID_PARAM.getMsg());
        }

        LocalDate date = labelReq.getDate();
        String grade = labelReq.getGrade();
        String palletNumber = labelReq.getPalletNumber();

        //计数清0
        setNumberZero(grade);

        //获取发货LOT
        String lotCode = getLotCode(grade);

        //获取标识号
        String identifyingNumber = getIdentifyingNumber(date, grade);

        //获取连号
        String lh = getLh(grade);

        //流水号
        String serialNumber = getSerialNumber(grade);

        //获取二维码
        String qrCode = getQrCode(date, serialNumber, lh, lotCode, grade);

        Label label = new Label();
        label.setPalletNumber(palletNumber);
        label.setDate(labelReq.getDate());
        label.setGrade(labelReq.getGrade());
        label.setLot(lotCode);
        label.setSerialNumber(serialNumber);
        label.setLh(lh);
        label.setIdentificationNumber(identifyingNumber);
        label.setCode(qrCode);
        label.setCode(qrCode);
        this.save(label);

        return ResultBody.success();
    }

    @Override
    public ResultBody getDateByPalletNumber(String palletNumber) {
        //获取最新的批次号
        String batchNumber = baseMapper.getBatchNumber(palletNumber);
        AssertUtil.isNotEmpty(batchNumber, "未获取到当前托盘号最新批次号!");
        //根据批次号获取最新的绑定时间
        if (StringUtil.isNotEmpty(batchNumber)) {
            Object dateObj = redisTemplate.opsForHash().get(RedisKey.BATCH_NUMBER_MAPPING_BIND_TIME, batchNumber);
            AssertUtil.isNotEmpty(dateObj, "未获取到当前托盘号最新绑定时间!");
            return ResultBody.success(dateObj);
        }
        return ResultBody.success();
    }

    @Override
    public IPage<Label> queryLabel(LabelReq labelReq) {
        final IPage<Label> page = new Page<>(labelReq.getPageNo(), labelReq.getPageSize());
        return baseMapper.queryLabel(page, labelReq);
    }

    @Override
    public ResultBody getSerialNumByGrade(String grade) {
        Constants.GlassGrade.getCodeByName(grade);
        //key
        String lotKey = String.format(RedisKey.QUALITY_LABEL_LOT_KEY, grade);
        String serialKey = String.format(RedisKey.QUALITY_LABEL_SERIAL_KEY, grade);
        String lhKey = String.format(RedisKey.QUALITY_LABEL_LH_KEY, grade);
        //value
        String lot = RedisUtils.get(lotKey);
        String serial = RedisUtils.get(serialKey);
        String lh = RedisUtils.get(lhKey);

        Map<String, Integer> dataMap = new HashMap<>();
        dataMap.put(lotKey, StringUtil.isNotEmpty(lot) ? Integer.parseInt(lot) : 0);
        dataMap.put(serialKey, StringUtil.isNotEmpty(serial) ? Integer.parseInt(serial) : 0);
        dataMap.put(lhKey, StringUtil.isNotEmpty(lh) ? Integer.parseInt(lh) : 0);


        return ResultBody.success(dataMap);
    }

    @Override
    public ResultBody editSerialNumBer(LabelSerialNumReq labelSerialNumReq) {
        String lotKey = String.format(RedisKey.QUALITY_LABEL_LOT_KEY, labelSerialNumReq.getGrade());
        String serialKey = String.format(RedisKey.QUALITY_LABEL_SERIAL_KEY, labelSerialNumReq.getGrade());
        String lhKey = String.format(RedisKey.QUALITY_LABEL_LH_KEY, labelSerialNumReq.getGrade());
        RedisUtils.set(lotKey, labelSerialNumReq.getLot());
        RedisUtils.set(serialKey, labelSerialNumReq.getSerialNumber());
        RedisUtils.set(lhKey, labelSerialNumReq.getLh());
        return ResultBody.success();
    }

    @Override
    public ResultBody likePalletNumber(String highlight) {
        if (StringUtil.isNotEmpty(highlight)) {
            return ResultBody.success(baseMapper.likePalletNumber(highlight));
        }
        return ResultBody.success();
    }

    @Override
    public void export(LabelReq labelReq, HttpServletResponse response) {
        labelReq.setPageNo(-1);
        labelReq.setPageSize(-1);
        IPage<Label> iPage = this.queryLabel(labelReq);
        if (iPage != null && CollectionUtils.isNotEmpty(iPage.getRecords())) {
            List<Label> labels = iPage.getRecords();
            try {
                EasyExcel.write(response.getOutputStream(), Label.class)
                        .excelType(ExcelTypeEnum.XLSX)
                        .registerWriteHandler(ExcelStyleUtils.createTableStyle())
                        .sheet("标签数据").doWrite(labels);
            } catch (IOException e) {
                log.error("数据导出失败：{}", e.getMessage());
            }
        }
    }


    /**
     * @Description: 批次号任务
     * @version v1.0
     * @author songJinKang
     * @date 2023-12-12 10:06
     */
    @Scheduled(cron = "0 */3 * * * ?")
    public void latestBindTimeTask() {
        getLatestBindTime();
    }

    private void getLatestBindTime() {
        List<LatestBindTime> latestBindTime = baseMapper.getLatestBindTime();
        if (CollectionUtils.isNotEmpty(latestBindTime)) {
            Map<String, String> latestBindTimeMap = latestBindTime.stream().collect(Collectors.toMap(LatestBindTime::getShelfLotId, e -> e.getBindTime().toString()));
            redisTemplate.opsForHash().putAll(RedisKey.BATCH_NUMBER_MAPPING_BIND_TIME, latestBindTimeMap);
        }
    }

    /**
     * @Description: 生成二维码
     * @version v1.0
     * @author songJinKang
     * @date 2023-12-08 15:25
     */
    private String getQrCode(LocalDate date, String serialNumber, String lh, String lotCode, String grade) {
        StringBuilder stringBuilder = new StringBuilder();
        //年月日
        stringBuilder.append(date.format(Constants.formatter));
        //无指定
        stringBuilder.append("*************");
        //glassBox容纳数量
        stringBuilder.append(280);
        //供应商代码
        stringBuilder.append("S");
        //生产地
        stringBuilder.append("S");
        //glass类型
        stringBuilder.append("G");
        //年份最后一位
        stringBuilder.append(String.valueOf(date.getYear()).substring(3));
        //月份
        stringBuilder.append(convertNumber(date.getMonthValue()));
        //流水号
        stringBuilder.append(serialNumber);
        //母板产地代码
        stringBuilder.append(2);
        //加工line代码
        stringBuilder.append(2);
        //玻璃种类
        stringBuilder.append(2226);
        //玻璃厚度
        stringBuilder.append(50);
        //连号
        stringBuilder.append(lh);
        //无指定
        stringBuilder.append("****************************************");
        //玻璃厂商代码
        stringBuilder.append(4);
        //glass批次号
        stringBuilder.append(lotCode);
        //glass基本size
        stringBuilder.append("J");
        //glass种类
        stringBuilder.append("G");
        //品级
        stringBuilder.append(Constants.GlassGrade.getCodeByName(grade));
        //grade2
        stringBuilder.append(1);
        //流水号
        stringBuilder.append(serialNumber);
        //glassBox容纳数量
        stringBuilder.append(280);
        //定位角代码
        stringBuilder.append(1);
        //预备
        stringBuilder.append(0);
        //无指定
        stringBuilder.append("*********************");
        return stringBuilder.toString();
    }


    /**
     * @Description: 生成流水号
     * @version v1.0
     * @author songJinKang
     * @date 2023-12-08 15:25
     */
    private String getSerialNumber(String grade) {
        String key = String.format(RedisKey.QUALITY_LABEL_SERIAL_KEY, grade);
        String serialStr = RedisUtils.get(key);
        if (StringUtil.isNotEmpty(serialStr)) {
            return padLeftZero(Integer.parseInt(serialStr), 4);
        }
        return null;
    }


    /**
     * @Description: 生成连号
     * @version v1.0
     * @author songJinKang
     * @date 2023-12-08 15:20
     * C固定 001到999
     */
    private String getLh(String grade) {
        StringBuilder stringBuilder = new StringBuilder();
        //固定字符
        stringBuilder.append("C");
        //序号
        String key = String.format(RedisKey.QUALITY_LABEL_LH_KEY, grade);
        long num = redisService.incr(key, Constants.ONE_INT);
        stringBuilder.append(padLeftZero((int) num, 3));
        return stringBuilder.toString();
    }

    /**
     * @Description: 生成发货LOT
     * @version v1.0
     * @author songJinKang
     * @date 2023-12-08 15:12
     * LOT号：S1B21(固定)C(月份)82（流水号：01~99，递增，循环）
     */
    private String getLotCode(String grade) {
        StringBuilder stringBuilder = new StringBuilder();
        //固定字符
        stringBuilder.append("S1B21");
        //品级编号 TFT=T、CF=C、Dummy=D
        if (Constants.GlassGrade.CF.getName().equals(grade)) {
            stringBuilder.append("C");
        } else if (Constants.GlassGrade.TFT.getName().equals(grade)) {
            stringBuilder.append("T");
        } else if (Constants.GlassGrade.DUMMY.getName().equals(grade)) {
            stringBuilder.append("D");
        }
        //序号
        String key = String.format(RedisKey.QUALITY_LABEL_LOT_KEY, grade);
        long num = redisService.incr(key, Constants.ONE_INT);
        stringBuilder.append(padLeftZero((int) num, 2));
        return stringBuilder.toString();
    }

    /**
     * @Description: 生成标识号
     * @version v1.0
     * @author songJinKang
     * @date 2023-12-08 15:12
     * 标识号：SSG(固定)3(年份)C(月份)0541(流水号：0001~9999，递增，循环)；
     */
    private String getIdentifyingNumber(LocalDate date, String grade) {
        StringBuilder stringBuilder = new StringBuilder();
        //固定字符
        stringBuilder.append("SSG");
        //年
        stringBuilder.append(String.valueOf(date.getYear()).substring(3));
        //月
        stringBuilder.append(convertNumber(date.getMonthValue()));
        //序号
        String key = String.format(RedisKey.QUALITY_LABEL_SERIAL_KEY, grade);
        long num = redisService.incr(key, Constants.ONE_INT);
        stringBuilder.append(padLeftZero((int) num, 4));
        return stringBuilder.toString();
    }


    /**
     * @Description: 根据将执行完的序号清0
     * @version v1.0
     * @author songJinKang
     * @date 2023-12-08 15:48
     * 标识号 == 9999
     * 连号 == 999
     * 流水号 == 9999
     * LOT = 99
     */
    private void setNumberZero(String grade) {
        String lotKey = String.format(RedisKey.QUALITY_LABEL_LOT_KEY, grade);
        String serialKey = String.format(RedisKey.QUALITY_LABEL_SERIAL_KEY, grade);
        String lhKey = String.format(RedisKey.QUALITY_LABEL_LH_KEY, grade);
        //LOT
        String lot = RedisUtils.get(lotKey);
        if (StringUtil.isNotEmpty(lot) && Integer.parseInt(lot) >= 99) {
            RedisUtils.set(lotKey, 0);
        }
        //流水号
        String serial = RedisUtils.get(serialKey);
        if (StringUtil.isNotEmpty(serial) && Integer.parseInt(serial) >= 9999) {
            RedisUtils.set(serialKey, 0);
        }
        //连号
        String lh = RedisUtils.get(lhKey);
        if (StringUtil.isNotEmpty(lh) && Integer.parseInt(lh) >= 999) {
            RedisUtils.set(lhKey, 0);
        }
    }

    public static String convertNumber(int number) {
        if (number >= 1 && number <= 9) {
            return String.valueOf(number);
        } else if (number >= 10 && number <= 12) {
            char letter = (char) ('A' + (number - 10));
            return String.valueOf(letter);
        }
        return null;
    }

    public static String padLeftZero(int number, int length) {
        return String.format("%0" + length + "d", number);
    }


}



